package ppOO;

public class ContPerceptron extends General {
   private double[] weights = null;
   private double output = 0;
   private double[] input = null;
   private boolean isInputBias = false;
   private final double bias = -1;
   private double errorSignalTerm = 0;

   private int layer_index = 0;
   private int perceptron_index = 0;

   public ContPerceptron(double[] weights,
         boolean isInputBiasNode, int neuronIndex,
         int layerIndex) { // used when initialising the layer
      if (isInputBiasNode != false) {
         setWeights(weights);
         setInputBias(isInputBiasNode);
      } else {
         setWeights(weights);
      }
      setPerceptronIndex(neuronIndex);
      setLayerIndex(layerIndex);

   }

   public ContPerceptron(double[] weights, double[] input,
         int neuronIndex, int layerIndex) {
      setWeights(weights);
      setInput(input);
      setPerceptronIndex(neuronIndex);
      setLayerIndex(layerIndex);
   }

   public double calcOutput() {
      double sum = 0;

      if (this.isInputBias == false) {
         if (this.input == null) {
            System.err
                  .println("err: input not set cannot compute neural network output");
         } else {
            if (this.input.length != this.weights.length) {
               System.err
                     .println("err: input array("
                           + input.length
                           + ") does not map correctly with weighting array("
                           + this.weights.length + ") ");
            }

            for (int i = 0; i < weights.length; i++) {
               sum += input[i]
                     * this.weights[i];

            }

            this.output = ((1 - Math.exp(-sum)) / (1 + Math
                  .exp(-sum)));
         }
      } else {
         this.output = bias;
      }
      return output;
   }

   public void updateWeights(double learningRate,
         double[] errorSignalSum) {
      double[] weightsArray = new double[getWeights().length];

      if (weights != null) // bias neuron.. doesn't need to update its input
                           // weights..
      {
         for (int i = (weights.length - 1); i > -1; i--) {

            weightsArray[i] = getWeights()[i]
                  + learningRate
                  * 0.5
                  * (errorSignalSum[perceptron_index] * (1 - Math
                        .pow(output, 2))) * getInput()[i];
            CPPrintln("LearningRate: "
                  + learningRate
                  + "\terrorSigSum: "
                  + printArray(errorSignalSum)
                  + "\tinput: "
                  + getInput()[i]
                  + "\tweight: "
                  + weightsArray[i]
                  + "\tweight change: "
                  + learningRate
                  * 0.5
                  * (errorSignalSum[perceptron_index] * (1 - Math
                        .pow(output, 2))) * getInput()[i]);
         }
         setWeights(weightsArray);
      }
   }

   public void updateWeightsOutputLayer(
         double learningRate, double desired) {
      // OutputLayer only
      double[] weightsArray = new double[getWeights().length];
      for (int i = (weights.length - 1); i > -1; i--) {
         weightsArray[i] = weightsArray[i]
               + (0.5 * learningRate) * (desired - output)
               * input[i];
      }
      setWeights(weightsArray);
   }

   /* Object parameter access */
   public void setInput(double[] inputArray) {
      this.input = inputArray;
   }

   public double[] getInput() {
      return this.input;
   }

   public void setWeights(double[] inputWeights) {
      this.weights = inputWeights;
   }

   public double[] getWeights() {
      return this.weights;
   }

   private void setInputBias(boolean isInputBiasNode) {
      this.isInputBias = isInputBiasNode;
   }

   private boolean getInputBias() {
      return this.isInputBias;
   }

   public void setLayerIndex(int index) {
      this.layer_index = index;
   }

   public int getLayerIndex() {
      return this.layer_index;
   }

   private void setPerceptronIndex(int index) {
      this.perceptron_index = index;
   }

   public int getPerceptronIndex() {
      return perceptron_index;
   }

   public boolean isInputBias() {
      return this.isInputBias;
   }

   public double getErrorSignalTerm() {
      return errorSignalTerm;
   }

   public void setErrorSignalTerm(double errorSignalTerm) {
      this.errorSignalTerm = errorSignalTerm;
   }

   private void CPPrintln(String output) {
      output += "\n";
      CPPrint(output);
   }

   private void CPPrint(String output) {
      if (DEBUG
            && DETAILED_DEBUG)
         System.out.print("DEBUG Cont Perceptron ("
               + getPerceptronIndex() + "/"
               + getLayerIndex() + "):\t" + output);
   }

}
